﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Xml.Linq;
using System.Text.RegularExpressions;
using System.Configuration;
using VPLink2013.Framework;

namespace VPLink2013.Batch
{
    public class Program
    {
        private static string vplinkUrl = string.Empty;
        private static string trombiXmlFile = string.Empty;
        private static int colCount = 0;
        private static Dictionary<string, string> dictTeamCoord = new Dictionary<string, string>();
        private static List<string> dirs;
        private static SearchTeams st;
        private static LDAPManager ldapManager;
        private static string picLibUrl = string.Empty;
        private static string myVPLinkUrl = string.Empty;
        private static string extraFilter = string.Empty;

        #region XML Helper
        private static XElement checkService(XElement parent, string serviceName, string code, int level)
        {
            XElement serviceXml = null;
            try
            {
                serviceXml = parent.Elements("Service")
                                    .Where(x => x.Attribute("Name").Value.ToUpper() == serviceName.ToUpper()).FirstOrDefault();
            }
            catch (Exception e)
            {
                Console.WriteLine("exception check service {0}", e.Message);
                
                ContextTrace.AddTrace(string.Format("Exception lors de la recherche du service {0}", serviceName));
                ContextTrace.AddTrace(string.Format("exception check service {0}", e.Message));
            }
            if (serviceXml == null)
            {
                serviceName = level < 4 ? serviceName.ToLower() : serviceName;
                serviceXml = XmlManager.CreateNode("Service", new Dictionary<string, string>() { 
									{"Name", serviceName },
									{"Code", code},
									{"ServiceLevel", level.ToString()}
								});
                parent.Add(serviceXml);
                ReorderXElementChildrenByServiceName(parent);
            }
            else
            {
                ContextTrace.AddTrace(string.Format("Le service {0} existe déjà", serviceName));
                if (serviceXml.isAttributeValue("FixedService", "True"))
                {
                    ContextTrace.AddTrace(string.Format("Le service {0} est FixedService", serviceName));
                    ReorderXElementChildrenByServiceName(parent);
                }
                if (serviceXml.isAttributeValue("Fixed", "True"))
                {
                    ContextTrace.AddTrace(string.Format("Le service {0} est Fixed", serviceName));
                    ReorderXElementChildrenByServiceName(parent);
                }
            }
            return serviceXml;
        }

        private static void ReorderXElementChildrenByServiceName(XElement parent)
        {
            var tmpAllElemts = parent.Elements()
                .Where(x => x.Name.LocalName.Equals("Service") && !x.isAttributeValue("Top", "True"))
                .OrderBy(x => x.Attribute("Name").Value).ToArray();

            parent.Elements()
            .Where(x => x.Name.LocalName.Equals("Service") && !x.isAttributeValue("Top", "True"))
            .Remove();
            foreach (XElement el in tmpAllElemts)
            {
                parent.Add(el);
            }
        }


        private static XElement CreateUserXElement(XElement parentService, Employee user,
            string emplLevel, string serviceLevel, ref bool exists)
        {
            XElement xmlUser = parentService.GetUniqueElementByAttributes("User",
                new Dictionary<string, string>(){{"LoginUser", user.Login}
			});

            if (xmlUser == null)
            {
                exists = false;
                string userTeamCode = Regex.Replace(user.TeamCode, @"\s+", string.Empty).Replace(Constants.LDAP_Init_Prefix, string.Empty);
                string teamCode = Regex.Replace(parentService.Attribute("Code").Value, @"\s+", string.Empty);
                bool isManagerLevel = false;
                bool isSecondLevelManagerInService = false;
                string parentSceLevel = parentService.Attribute("ServiceLevel").Value;

                isManagerLevel = user.IsManager && Int32.Parse(emplLevel) <= Int32.Parse(parentSceLevel);
                isSecondLevelManagerInService = (user.IsManager
                    && ((Int32.Parse(parentSceLevel) == 3 && (Int32.Parse(emplLevel) == 4 || Int32.Parse(emplLevel) == 5))
                        || (Int32.Parse(parentSceLevel) == 4 && Int32.Parse(emplLevel) == 5)));

                return XmlManager.CreateNode("User", new Dictionary<string, string>() { 
											{"Level", emplLevel},
											{"ServiceLevel", serviceLevel},
											{"RowNumber", "0" },
											{"ColumnNumber", "0" },
											{"LoginUser", user.Value},
											{"Manager", user.ManagerValue},
											{"FirstName", user.FirstName},
											{"LastName", user.LastName},
											{"IsManager", user.IsManager.ToString()},
											{"PictureURL", user.PictureUrl},
											{"WorkPhone", user.PhoneNumber },
											{"UrlSitePersonnel", user.SitePersonnel},
											{"Title", user.Title },
											{"IsManagerLevel", isManagerLevel.ToString()},
											{"isSecondLevelManagerInService", isSecondLevelManagerInService.ToString()}
				});
            }
            else
            {
                exists = true;
                return xmlUser;
            }
        }

        #endregion

        private static int CompareTeamsNames(TeamFromOrganisationList t1, TeamFromOrganisationList t2)
        {
            if (t1.DirectionName.Equals(t2.DirectionName))
            {
                if (t1.DepartementName.Equals(t2.DepartementName))
                {
                    return t1.ServcieName.CompareTo(t2.ServcieName);
                }
                else
                {
                    return t1.DepartementName.CompareTo(t2.DepartementName);
                }
            }
            else
            {
                return t1.DirectionName.CompareTo(t2.DirectionName);
            }
        }


        #region File Helper
        private static void SaveXmlToFile(XDocument trombiXml)
        {
            using (SPSite site = new SPSite(vplinkUrl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    SPFile file = web.GetFile(trombiXmlFile);
                    file.SaveBinary(Encoding.UTF8.GetBytes(trombiXml.ToString()));
                }
            }
        }

        private static string GetTrombiXmlFileAsString()
        {
            string vplinkTrombiXmlAsString = string.Empty;

            using (SPSite site = new SPSite(vplinkUrl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    vplinkTrombiXmlAsString = web.GetFileAsString(trombiXmlFile);
                }
            }
            return vplinkTrombiXmlAsString;
        }
        #endregion

        private static string GetUserLevelFromTeam(Employee user, int initlevel)
        {
            if (!string.IsNullOrEmpty(user.ManagerValue))
            {
                Employee manager = ldapManager.GetEmployeeByValue(user.ManagerValue, extraFilter);
                if (manager != null)
                {
                    if (string.Compare(manager.ManagerValue, user.Value, StringComparison.CurrentCultureIgnoreCase) != 0)
                    {
                        string managerLevel = GetUserLevel(manager, false);
                        if (!string.IsNullOrEmpty(managerLevel))
                        {
                            int level = Int32.Parse(managerLevel) + 1;
                            return level.ToString();
                        }
                        else
                        {
                            return initlevel.ToString();
                        }
                    }
                    else
                    {
                        initlevel++;
                        return initlevel.ToString();
                    }
                }
                else
                {
                    return initlevel.ToString();
                }
            }
            else
            {
                return initlevel.ToString();
            }
        }

        private static string teamCodeToCompare(string teamCode)
        {
            if (teamCode.StartsWith(Constants.LDAP_SCE_CODE_Prefix))
            {
                return Constants.LDAP_SCE_CODE_Prefix.Substring(0, 6);
            }
            else
                return teamCode.Substring(0, 5);
        }

        private static string GetUserLevel(Employee user, bool serviceLevel)
        {
            string level = string.Empty;
            if (dirs.Any(x => user.Value.Equals(x, StringComparison.CurrentCultureIgnoreCase)))
            {
                level = "1";
            }
            else
            {
                if (!string.IsNullOrEmpty(user.TeamCode))
                {
                    switch (teamCodeToCompare(user.TeamCode))
                    {
                        case Constants.LDAP_DIR_CODE_Prefix:
                            {
                                level = serviceLevel ? "2" : GetUserLevelFromTeam(user, 2);
                                break;
                            }
                        case Constants.LDAP_DPT_CODE_Prefix:
                            {
                                level = serviceLevel ? "3" : GetUserLevelFromTeam(user, 3);
                                break;
                            }
                        case Constants.LDAP_SCE_CODE_Prefix:
                            {
                                level = serviceLevel ? "4" : GetUserLevelFromTeam(user, 4);
                                break;
                            }
                    }
                }
            }
            return level;
        }

        private static void FlagParents(XElement elem)
        {
            if (elem.isAttributeValue("Fixed", "True"))
            {
                elem.SetAttributeToParents("Services", "Fixed", "True");
            }
        }

        private static void AddUserXElementToTeamXml(XElement teamXml, List<XElement> usersXelements, Employee user, string level)
        {
            string serviceLevel = GetUserLevel(user, true);
            bool exists = false;
            XElement userXml = usersXelements.Where(x => x.Attribute("LoginUser").Value.Equals(user.Value)).FirstOrDefault();
            if (userXml == null)
            {
                // If employee not specified
                userXml = CreateUserXElement(teamXml, user, level, serviceLevel, ref exists);
                if (!exists)
                    teamXml.Add(userXml);
                usersXelements.Add(userXml);
            }
        }

        private static void CreateDirectionsNodes(XDocument vplinkTrombiXml)
        {
            List<TeamFromOrganisationList> directions = st.GetAllTeamsByPrefix(Constants.LDAP_DIR_CODE_Prefix.Replace(Constants.LDAP_Init_Prefix, string.Empty));
            directions.Sort((x, y) => CompareTeamsNames(x, y));
            foreach (TeamFromOrganisationList dir in directions)
            {
                Console.WriteLine("Direction : {0}", dir.DirectionName);
                ContextTrace.AddTrace(string.Format("Direction : {0}", dir.DirectionName));
                List<TeamFromOrganisationList> dirTeams = st.GetAllTeamsByDirection(Constants.OrganisationDirectionField, dir.DirectionName);
                List<string> codes = dirTeams.Select(x => x.CodeTeam).ToList();
                XElement directionXml = checkService(vplinkTrombiXml.Root, dir.DirectionName, dir.CodeTeam, 2);
                if (!directionXml.isAttributeValue("FixedService", "True"))
                {
                    List<XElement> fixedUsers = directionXml.GetAllElementsByAttributes("User", new Dictionary<string, string>() { 
											{"Fixed", "True"} });
                    List<XElement> usersXelements = new List<XElement>();
                    usersXelements = fixedUsers;
                    List<Employee> dirEmployees = ldapManager.GetEmployeesByOUCode(codes, vplinkUrl, picLibUrl, extraFilter);
                    foreach (Employee user in dirEmployees)
                    {
                        string userTeamCode = Regex.Replace(user.TeamCode, @"\s+", string.Empty).Replace(Constants.LDAP_Init_Prefix, string.Empty);
                        string teamCode = Regex.Replace(dir.CodeTeam, @"\s+", string.Empty);
                        string level = GetUserLevel(user, false);
                        if (
                            //user.IsManager == true && teamCode.Equals( userTeamCode )
                            teamCode.Equals(userTeamCode)
                            || (Int32.Parse(level) < 4 && dirEmployees.Any(x => x.Value.Equals(user.ManagerValue)))
                            || (Int32.Parse(level) < 4 && user.IsManager)
                            || !st.DoesTeamHaveSubDirection(dir.DirectionName, Constants.OrganisationDirectionField)
                            || (Int32.Parse(level) < 5 && dir.CodeTeam.Equals("DIR - DRH", StringComparison.CurrentCultureIgnoreCase)))
                        {
                            AddUserXElementToTeamXml(directionXml, usersXelements, user, level);
                        }
                    }
                    try
                    {
                        directionXml.CalculateEmployeesCoord(usersXelements, colCount);
                    }
                    catch (Exception ex)
                    {
                        ContextTrace.AddTrace(string.Format("Exception lors du calcul de {0}", dir.DirectionName));
                        ContextTrace.AddTrace(string.Format("Exception {0}", ex.Message));
                    }
                }
            }
        }

        private static bool IsManagerInGroup(string managerValue, List<Employee> employees)
        {
            bool res = employees.Any(x => managerValue.Equals(x.Value));
            ContextTrace.AddTrace(string.Format("Manager {0} trouvé : {1}", managerValue, res));
            return res;
        }

        private static void CreateDepartementsNodes(XDocument vplinkTrombiXml)
        {
            List<TeamFromOrganisationList> departements = st.GetAllTeamsByPrefix(Constants.LDAP_DPT_CODE_Prefix.Replace(Constants.LDAP_Init_Prefix, string.Empty));
            departements.Sort((x, y) => CompareTeamsNames(x, y));
            foreach (TeamFromOrganisationList dept in departements)
            {
                Console.WriteLine("Departement : {0}", dept.DepartementName);
                ContextTrace.AddTrace(string.Format("Departement : {0}", dept.DepartementName));
                XElement directionXml = checkService(vplinkTrombiXml.Root, dept.DirectionName, string.Empty, 2);
                XElement departementXml = checkService(directionXml, dept.DepartementName, dept.CodeTeam, 3);
                if (!departementXml.isAttributeValue("FixedService", "True"))
                {
                    List<TeamFromOrganisationList> deptTeams = st.GetAllTeamsByDirection(Constants.OrganisationDepartementField, dept.DepartementName);
                    List<string> codes = deptTeams.Where(x => !x.CodeTeam.StartsWith(Constants.LDAP_DIR_CODE_Prefix)).Select(x => x.CodeTeam).ToList();

                    List<XElement> fixedUsers = departementXml.GetAllElementsByAttributes("User", new Dictionary<string, string>() { 
											{"Fixed", "True"} });
                    List<XElement> usersXelements = new List<XElement>();
                    usersXelements = fixedUsers;
                    List<Employee> deptEmployees = ldapManager.GetEmployeesByOUCode(codes, vplinkUrl, picLibUrl, extraFilter);

                    ContextTrace.AddTrace(string.Format("Departement : {0}", dept.DepartementName));
                    int nbTopManagers = deptEmployees.Where(x => x.IsManager && !IsManagerInGroup(x.ManagerValue, deptEmployees)).Distinct().Count();
                    ContextTrace.AddTrace(string.Format("nombre top managers : {0}", nbTopManagers));

                    foreach (Employee user in deptEmployees)
                    {
                        Console.WriteLine("Creation de Employee xml {0}", user.FullName);
                        string userTeamCode = Regex.Replace(user.TeamCode, @"\s+", string.Empty).Replace(Constants.LDAP_Init_Prefix, string.Empty);
                        string teamCode = Regex.Replace(dept.CodeTeam, @"\s+", string.Empty);
                        string level = GetUserLevel(user, false);

                        if (
                            //( Int32.Parse( level ) < 5 && deptEmployees.Any( x => x.Value.Equals( user.ManagerValue ) ) )
                            (!st.DoesTeamHaveSubDirection(dept.DepartementName, Constants.OrganisationDepartementField))
                            || Int32.Parse(level) < 4
                            || Int32.Parse(level) < 5 && nbTopManagers == 1
                            || Int32.Parse(level) < 5 && nbTopManagers > 1 && dept.CodeTeam.Equals("DPT - COM2FR", StringComparison.CurrentCultureIgnoreCase)
                            || Int32.Parse(level) < 5 && nbTopManagers > 1 && dept.CodeTeam.Equals("DPT - SRM", StringComparison.CurrentCultureIgnoreCase)
                            || Int32.Parse(level) < 6 && dept.CodeTeam.Equals("DPT - DF", StringComparison.CurrentCultureIgnoreCase))
                        //|| ( ( user.IsManager == true ) && Int32.Parse( level ) < 5 )
                        //|| ( !string.IsNullOrEmpty( user.ManagerValue ) && teamCode.Equals( userTeamCode ) ) )
                        {
                            AddUserXElementToTeamXml(departementXml, usersXelements, user, level);
                        }
                    }
                    try
                    {
                        departementXml.CalculateEmployeesCoord(usersXelements, colCount);
                    }
                    catch (Exception ex)
                    {
                        ContextTrace.AddTrace(string.Format("Exception lors du calcul de {0}", dept.DepartementName));
                        ContextTrace.AddTrace(string.Format("Exception {0}", ex.Message));
                    }
                }
            }
        }

        private static void CreateServicesNodes(XDocument vplinkTrombiXml)
        {
            List<TeamFromOrganisationList> services = st.GetAllTeamsByPrefix(Constants.LDAP_SCE_CODE_Prefix.Replace(Constants.LDAP_Init_Prefix, string.Empty));
            services.Sort((x, y) => CompareTeamsNames(x, y));
            foreach (TeamFromOrganisationList sce in services)
            {
                Console.WriteLine("Service : {0}", sce.ServcieName);
                ContextTrace.AddTrace(string.Format("Service : {0}", sce.ServcieName));
                XElement directionXml = checkService(vplinkTrombiXml.Root, sce.DirectionName, string.Empty, 2);
                XElement departementXml = checkService(directionXml, sce.DepartementName, string.Empty, 3);
                XElement serviceXml = checkService(departementXml, sce.ServcieName, sce.CodeTeam, 4);
                if (!serviceXml.isAttributeValue("FixedService", "True"))
                {
                    List<XElement> fixedUsers = serviceXml.GetAllElementsByAttributes("User", new Dictionary<string, string>() { 
											{"Fixed", "True"} });
                    List<XElement> usersXelements = new List<XElement>();
                    usersXelements = fixedUsers;
                    List<Employee> sceEmployees = ldapManager.GetEmployeesByOUCode(new List<string> { sce.CodeTeam }, vplinkUrl, picLibUrl, extraFilter);
                    foreach (Employee user in sceEmployees)
                    {
                        if (user.IsManager == true || !string.IsNullOrEmpty(user.ManagerValue))
                        {
                            string level = GetUserLevel(user, false);
                            AddUserXElementToTeamXml(serviceXml, usersXelements, user, level);
                        }
                    }
                    try
                    {
                        serviceXml.CalculateEmployeesCoord(usersXelements, colCount);
                    }
                    catch (Exception ex)
                    {
                        ContextTrace.AddTrace(string.Format("Exception lors du calcul de {0}", sce.ServcieName));
                        ContextTrace.AddTrace(string.Format("Exception {0}", ex.Message));
                    }
                }
            }
        }


        private static void setConfigVariables()
        {
            vplinkUrl = ConfigurationSettings.AppSettings["urlSite"];
            trombiXmlFile = ConfigurationSettings.AppSettings["xmlFile"];
            colCount = Int32.Parse(ConfigurationSettings.AppSettings["ColNum"]);
            picLibUrl = ConfigurationSettings.AppSettings["picLibUrl"];
            myVPLinkUrl = ConfigurationSettings.AppSettings["myvplinkUrl"];
            string dirLogins = ConfigurationSettings.AppSettings["DirLogins"];
            extraFilter = ConfigurationSettings.AppSettings["extraLdapFilter"];
            dirs = new List<string>(dirLogins.Split(';'));
        }

        static void Main(string[] args)
        {
            setConfigVariables();
            int ldapPort = Int32.Parse(ConfigurationSettings.AppSettings["ldapPortNumber"]);
            string ldapServerName = ConfigurationSettings.AppSettings["ldapServer"];
            string url2 = ConfigurationSettings.AppSettings["urlSite2"];

            string vplinkTrombiXmlAsString = GetTrombiXmlFileAsString();
            if (!string.IsNullOrEmpty(vplinkTrombiXmlAsString))
            {
                XDocument vplinkTrombiXml = XDocument.Parse(vplinkTrombiXmlAsString);

                FixServices(vplinkTrombiXml);

                st = new SearchTeams(vplinkUrl);
                ldapManager = new LDAPManager(ldapServerName, ldapPort, url2, picLibUrl, myVPLinkUrl, extraFilter);
                CreateDirectionsNodes(vplinkTrombiXml);
                CreateDepartementsNodes(vplinkTrombiXml);
                CreateServicesNodes(vplinkTrombiXml);
                SaveXmlToFile(vplinkTrombiXml);
            }
            ContextTrace.Save("","updatetrombi_trace");
        }

        private static void FixServices(XDocument vplinkTrombiXml)
        {
            vplinkTrombiXml.Descendants().ToList<XElement>()
                            .ForEach(x => FlagParents(x));

            XDocument rootCopy = new XDocument(vplinkTrombiXml);
            rootCopy.Root.Elements().Remove();

            vplinkTrombiXml.Root.Elements()
                .Where(x => !x.isAttributeValue("FixedService", "True")
                    && !x.isAttributeValue("Fixed", "True")).Remove();

            vplinkTrombiXml.Root.Elements()
                .Where(x => x.isAttributeValue("Fixed", "True"))
                .Descendants().Where(x => !x.isAttributeValue("Fixed", "True")).Remove();
        }
    }
}